﻿using System;
using System.Text;
using System.Text.RegularExpressions;

namespace mklib.json
{
	/// <summary>
	/// Json文本读取
	/// </summary>
	internal class JsonTokenReader
	{
		internal JsonTokenReader(StringBuilder builder)
		{
			if (builder == null)
				throw new ArgumentNullException ("builder", "初始化字符串不能为空");
			this.s = builder;
			this.pos = 0;
		}
		private readonly StringBuilder s;
		private int pos;

		/// <summary>
		/// 是否到文档结尾
		/// </summary>
		/// <value><c>true</c> if EO; otherwise, <c>false</c>.</value>
		private bool EOF
		{
			get{ return pos >= s.Length; }
		}

		/// <summary>
		/// 当前字符
		/// </summary>
		/// <value>The current char.</value>
		private char CurrentChar{
			get{ 
				return pos >= s.Length ? char.MinValue : s [pos];
			}
		}

		public IJsonValue Parse()
		{
			var type = ReadTest (s, ref pos);
			JsonValue val = ReadKeyword (type, 0);
			if (!this.EOF) {
				ReadBlank (s, ref pos);
				if (!this.EOF) {
					throw new JsonParseException (this.CurrentChar);
				}
			}
			return val;
		}

		private JsonValue ReadKeyword(JsonTokenChar type, int depth)
		{
			string str;
			object val;
			JsonValueType t;
			switch (type) {
			case JsonTokenChar.Quote:
				str = ReadQuote (s, this.CurrentChar, ref pos);
				val = str;
				t = JsonValueType.String;
				break;
			case JsonTokenChar.Number:
				str = ReadNumber (s, ref pos);
				val = str;
				t = str.IndexOf ('.') > -1 ? JsonValueType.Float : JsonValueType.Int;
				break;
			case JsonTokenChar.Letter:
				str = ReadKeyword (s, ref pos);
				val = str;
				switch (str) {
				case "null":
					t = JsonValueType.Null;
					break;
				case "true":
				case "false":
					t = JsonValueType.Boolean;
					break;
				default:
					throw new JsonParseException ("unknow type[" + str + "]");
				}
				break;
			case JsonTokenChar.Object:
				if (this.CurrentChar == '{') {
					int a = pos;
					JsonValueObject obj = new JsonValueObject (depth + 1);
					ReadObject (obj, depth);
					str = Substring (s, a, pos);
					val = obj;
					t = JsonValueType.Object;
				} else if (this.CurrentChar == '[') {
					int a = pos;
					JsonValueArray arr = new JsonValueArray (depth + 1);
					ReadArray (arr, depth);
					str = Substring (s, a, pos);
					val = arr;
					t = JsonValueType.Array;
				} else {
					throw new JsonParseException (this.CurrentChar);
				}
				break;
			default:
				throw new JsonParseException (String.Format("unknow type [{0}]", type));
			}

			return new JsonValue (val, t, str, depth);
		}

		/// <summary>
		/// 读取对象类型
		/// </summary>
		/// <param name="obj">Object.</param>
		private void ReadObject(JsonValueObject obj, int depth)
		{
			pos++;
			ReadObjectItem (obj, depth + 1);

			var type = ReadTest (s, ref pos);
			if (this.CurrentChar == '}'){
				pos++;
				return;
			} else if(type== JsonTokenChar.Comma){
				ReadObject(obj, depth);
			} else {
				throw new JsonParseException(this.CurrentChar);
			}
		}

		/// <summary>
		/// 读取对象内部元素
		/// </summary>
		/// <param name="obj">Object.</param>
		private void ReadObjectItem(JsonValueObject obj, int depth)
		{
			var type = ReadTest (s, ref pos);
			if (JsonTokenHelper.IsEnd (type) && this.CurrentChar == '}') {
				return;
			}
			if (type != JsonTokenChar.Quote) {
				throw new JsonParseException (this.CurrentChar);
			}
			var key = ReadQuote (s, this.CurrentChar, ref pos);
			type = ReadTest (s, ref pos);

			if (type != JsonTokenChar.Colon) {
				throw new JsonParseException (this.CurrentChar);
			}

			pos++;
			type = ReadTest (s, ref pos);
			var val = ReadKeyword (type, depth);

			var v = val as IJsonValue;
			obj[key] = val;
		}

		/// <summary>
		/// 读取数组
		/// </summary>
		/// <param name="arr">Arr.</param>
		private void ReadArray(JsonValueArray arr, int depth)
		{
			pos++;
			ReadArrayItem (arr, depth + 1);

			var type = ReadTest (s, ref pos);
			if (this.CurrentChar == ']'){
				pos++;
				return;
			} else if(type== JsonTokenChar.Comma){
				ReadArray(arr, depth);
			} else {
				throw new JsonParseException(this.CurrentChar);
			}
		}

		/// <summary>
		/// 读取数组内部
		/// </summary>
		/// <param name="arr">Arr.</param>
		private void ReadArrayItem(JsonValueArray arr, int depth)
		{
			var type = ReadTest (s, ref pos);
			if (this.CurrentChar == ']') {
				return;
			}
			var val = ReadKeyword (type, depth);
			arr.Add (val);
		}

		#region 静态方法

		/// <summary>
		/// 读取到指定字符
		/// </summary>
		/// <returns>指定字符在当前字串中的索引位置</returns>
		/// <param name="s">读取的字符串</param>
		/// <param name="c">指定字符</param>
		/// <param name="i">起始索引</param>
		private static int ReadLineTo(StringBuilder s, char c, ref int i)
		{
			if (i < 0)
				i = 0;
			
			int len = s.Length;
			char C = char.MinValue;
			char t;
			while (i < len) {
				t = s [i];
				if (C != JsonTokenConfig.IgnoreChar) {
					if (t == c) {
						return i;
					} else if (JsonTokenHelper.IsBreakLine (t)) {
						throw new JsonParseException (t);
					}
				}
				C = t;
				i++;
			}
			return -1;
		}

		/// <summary>
		/// 读取到非空白字符
		/// </summary>
		/// <returns><c>true</c>, if blank was  read, <c>false</c> otherwise.</returns>
		/// <param name="s">S.</param>
		/// <param name="i">The index.</param>
		private static bool ReadBlank(StringBuilder s, ref int i)
		{

			if (i < 0)
				i = 0;

			int len = s.Length;
			while (i < len) {
				if (!JsonTokenHelper.IsBlankOrBreak (s [i]))
					return true;
				i++;
			}
			return false;
		}

		/// <summary>
		/// 在引号外，读取到下一个结构语义字符，包括空白字符。
		/// </summary>
		/// <returns>The breaker.</returns>
		/// <param name="s">S.</param>
		/// <param name="i">The index.</param>
		private static string ReadBreaker(StringBuilder s, ref int i)
		{
			int a = i;
			int len = s.Length;
			char c;
			while (i < len) {
				c = s [i];
				if (JsonTokenHelper.IsBreaker (s [i]))
					break;
				i++;
			}
			return Substring (s, a, i);
		}

		/// <summary>
		/// 从StringBuilder中分割一个字符串
		/// </summary>
		/// <param name="builder">Builder.</param>
		/// <param name="a">The alpha component.</param>
		/// <param name="b">The blue component.</param>
		private static string Substring(StringBuilder builder, int a, int b)
		{
			int c = b - a;
			char[] cs = new char[c];
			builder.CopyTo(a, cs, 0, c);
			string s = new string(cs);
			cs = null;
			return s;
		}

		/// <summary>
		/// 读取到下一个非空白字符，返回当前结构语义
		/// </summary>
		/// <returns>The test.</returns>
		/// <param name="s">S.</param>
		/// <param name="i">The index.</param>
		private static JsonTokenChar ReadTest(StringBuilder s, ref int i)
		{
			if (!ReadBlank (s, ref i))
				return JsonTokenChar.Invalid;
			return JsonTokenHelper.GetCharType (s [i]);
		}
	
		/// <summary>
		/// 读取一个引号内部文本
		/// </summary>
		/// <returns>The quote.</returns>
		/// <param name="s">S.</param>
		/// <param name="q">结束引号符，可单可双</param>
		/// <param name="i">The index.</param>
		private static string ReadQuote(StringBuilder s, char q, ref int i)
		{
			int a = ++i;
			int b = ReadLineTo (s, q, ref i);
			string r;
			if (b <= a) {
				r = null;
			} else {
				r = Substring (s, a, b);
				i++;
			}
			return r;
		}

		/// <summary>
		/// 读取一个数字，包括整数，浮点数，以及2E-2类型
		/// </summary>
		/// <returns>The number.</returns>
		/// <param name="s">S.</param>
		/// <param name="i">The index.</param>
		private static string ReadNumber(StringBuilder s, ref int i)
		{
			string r = ReadBreaker (s, ref i);
			if (JsonTokenConfig.NumberRegex.Match (r).Success) {
				return r;
			} else {
				return null;
			}
		}

		/// <summary>
		/// 读取直接量关键字。关键字定义见JsonConfig.Keyword
		/// </summary>
		/// <returns>The keyword.</returns>
		/// <param name="s">S.</param>
		/// <param name="i">The index.</param>
		private static string ReadKeyword(StringBuilder s, ref int i)
		{
			char c = s [i];
			string val = ReadBreaker (s, ref i);
			if (JsonTokenHelper.IsKeyword (val)) {
				return val;
			} else {
				throw new JsonParseException (c);
			}
		}

		#endregion
	}
}